package com.thoughtorigin.flux {
import com.thoughtorigin.flux.fluxmocks.Sample;

import com.thoughtorigin.flux.fluxmocks.SampleProperties;

import com.thoughtorigin.flux.fluxmocks.SampleWithConstructor;

import com.thoughtorigin.flux.fluxmocks.SampleWithRefConstructor;

import org.flexunit.asserts.assertEquals;
import org.flexunit.asserts.assertFalse;
import org.flexunit.asserts.assertNotNull;
import org.flexunit.asserts.assertTrue;
import org.flexunit.asserts.fail;

public class XMLFluxTest {

    private var xmlSettings: XML =
        <root>
            <Def name="sample" object="com.thoughtorigin.flux.fluxmocks.Sample" />
            <Def name="sample2" object="com.thoughtorigin.flux.fluxmocks.Sample2" />
            <Def name="umbrella" object="com.thoughtorigin.flux.fluxmocks.SampleProperties">
                <properties>
                    <property name="blam" value="kamblamo" />
                </properties>
            </Def>
            <Def name="sample3" object="com.thoughtorigin.flux.fluxmocks.SampleWithConstructor">
                <constructor>
                    <arg value="sampleArg" />
                    <arg value="sampleArg2" />
                </constructor>
            </Def>
            <Def name="sample4" object="com.thoughtorigin.flux.fluxmocks.SampleWithConstructor">
                <constructor>
                    <arg value="sampleArg" />
                </constructor>
            </Def>
            <Def name="sample5" object="com.thoughtorigin.flux.fluxmocks.SampleProperties">
                <functions>
                    <exec name="sampleFunction" />
                </functions>
            </Def>
            <Def name="sample6" object="com.thoughtorigin.flux.fluxmocks.SampleProperties">
                <functions>
                    <exec name="sampleArgFunction">
                        <arg value="value1" />
                        <arg value="value2" />
                    </exec>
                </functions>
            </Def>
            <Def name="sample7" object="com.thoughtorigin.flux.fluxmocks.SampleProperties">
                <properties>
                    <property name="sample" ref="sample" />
                </properties>
            </Def>
            <Def name="sample8" object="com.thoughtorigin.flux.fluxmocks.SampleWithRefConstructor">
                <constructor>
                    <arg ref="sample7" />
                </constructor>
            </Def>
            <Def name="sample9" object="com.thoughtorigin.flux.fluxmocks.SampleProperties">
                <functions>
                    <exec name="sampleArgsFunctionWithRef">
                        <arg ref="sample3" />
                    </exec>
                </functions>
            </Def>
            <Def name="sample10" object="com.thoughtorigin.flux.fluxmocks.Sample" createNew="true"/>
            <Def name="sample11" object="com.thoughtorigin.flux.fluxmocks.SampleProperties">
                <properties>
                    <property name="sample" ref="sample10" />
                </properties>
            </Def>
        </root>;

    private var _xmlIOC: XMLFlux;

    [Before]
    public function setup(): void {
        _xmlIOC = new XMLFlux(xmlSettings);
    }

    [Test]
    public function shouldReturnAValidProperty(): void {
        var property: Object = _xmlIOC.getProperty("sample");

        assertNotNull(property);
        assertTrue(property is com.thoughtorigin.flux.fluxmocks.Sample);
    }

    [Test]
    public function shouldThrowAnExceptionIfPropertyIsNotFound(): void {
        try {
            var property: Object = _xmlIOC.getProperty("cannot find this");
            fail();
        } catch(e: PropertyNotFoundException) {
            assertTrue(true);
        }
    }

    [Test]
    public function shouldThrowAnExceptionIfPropertyClassDoesntExist(): void {
        try {
            var property: Object = _xmlIOC.getProperty("sample2");
            fail();
        } catch(e: ClassDefinitionNotFoundException) {
            assertTrue(true);
        }

    }

    [Test]
    public function shouldOnlyCreateOneInstanceOfTheObject(): void {
        var propertyA: Object = _xmlIOC.getProperty("sample");
        var propertyB: Object = _xmlIOC.getProperty("sample");

        assertEquals(propertyA, propertyB);
    }

    [Test]
    public function shouldCreateAProperty(): void {
        var obj: Object = _xmlIOC.createNew("sample");
        assertNotNull(obj);
        assertTrue(obj is Sample);
    }

    [Test]
    public function shouldCreateANewInstanceEveryTime(): void {
        var propertyA: Object = _xmlIOC.createNew("sample");
        var propertyB: Object = _xmlIOC.createNew("sample");

        assertTrue(propertyA != propertyB);        
    }

    [Test]
    public function shouldReturnAClassType(): void {
        var clazz: Class = _xmlIOC.getClass("sample");
        assertNotNull(clazz);
        assertTrue(clazz is Class);
        assertTrue(new clazz() is Sample);
    }

    [Test]
    public function shouldThrowAnExceptionIfTheClassIsNotFound(): void {
        try {
            var property: Class = _xmlIOC.getClass("sample2");
            fail();
        } catch(e: ClassDefinitionNotFoundException) {
            assertTrue(true);
        }
    }

    [Test]
    public function shouldCreateAInstanceAndAssignProperties(): void {
        var obj: SampleProperties = SampleProperties(_xmlIOC.getProperty("umbrella"));
        assertNotNull(obj);
        assertEquals("kamblamo", obj.blam);
    }

    [Test]
    public function shouldAssignConstructorArgs(): void {
        var obj: SampleWithConstructor = SampleWithConstructor(_xmlIOC.getProperty("sample3"));
        assertEquals("sampleArg", obj.arg1);
        assertEquals("sampleArg2", obj.arg2);
    }

    [Test]
    public function shouldThrowExceptionIfArgMismatch(): void {
        try {
            var obj: SampleWithConstructor = SampleWithConstructor(_xmlIOC.getProperty("sample4"));
            fail();
        } catch (e: ArgumentError) {
            assertTrue(true);
        }
    }

    [Test]
    public function shouldExecuteFunctions(): void {
        var obj: SampleProperties = SampleProperties(_xmlIOC.getProperty("sample5"));
        assertTrue(obj.sampleFunctionValue);
    }

    [Test]
    public function shouldExecuteFunctionsWithArgs(): void {
        var obj: SampleProperties = SampleProperties(_xmlIOC.getProperty("sample6"));
        assertEquals("value1", obj.arg1);
        assertEquals("value2", obj.arg2);
    }

    [Test]
    public function shouldCreateInstanceAndPassReferenceVariablesAndAssignToProperties(): void {
        var obj: SampleProperties = SampleProperties(_xmlIOC.getProperty("sample7"));
        assertNotNull(obj.sample);
        assertTrue(obj.sample is Sample);
    }

    [Test]
    public function shouldCreateInstanceAndPassReferenceVariablesAndAssignToConstructor(): void {
        var obj: SampleWithRefConstructor = SampleWithRefConstructor(_xmlIOC.getProperty("sample8"));
        assertNotNull(obj.sample);
        assertNotNull(obj.sample.sample);
    }

    [Test]
    public function shouldCreateInstanceAndPassReferenceVariablesAndAssignToFunction(): void {
        var obj: SampleProperties = SampleProperties(_xmlIOC.getProperty("sample9"));
        assertNotNull(obj.sampleArgsRef);
        assertEquals("sampleArg", obj.sampleArgsRef.arg1);
    }

    [Test]
    public function shouldCreateOneSingleInstance(): void {
        var obj: Sample = Sample(_xmlIOC.getProperty("sample"));
        var obj2: SampleProperties = SampleProperties(_xmlIOC.getProperty("sample7"));

        assertEquals(obj, obj2.sample);
    }

    [Test]
    public function shouldCreateNewInstanceIfExplicitlyConfiguredToCreateNewInstances(): void {
        var obj: Sample = Sample(_xmlIOC.getProperty("sample10"));
        var obj2: Sample = Sample(_xmlIOC.getProperty("sample10"));

        assertFalse(obj == obj2);

        var obj3: Sample = Sample(_xmlIOC.getProperty("sample10"));
        var obj4: SampleProperties = SampleProperties(_xmlIOC.getProperty("sample11"));

        assertFalse(obj3 == obj4.sample);
    }

    [Test]
    public function shouldCreateNewInstanceAndNotOverwriteCache(): void {
        var obj: Sample = Sample(_xmlIOC.getProperty("sample"));
        var obj2: Sample = Sample(_xmlIOC.createNew("sample"));
        var obj3: Sample = Sample(_xmlIOC.getProperty("sample"));

        assertFalse(obj == obj2);
        assertEquals(obj, obj3);
    }

    [Test]
    public function shouldCreateNewInstanceWithAddtionalPropertiesAndExecutions(): void {
        var obj: SampleWithRefConstructor = SampleWithRefConstructor(_xmlIOC.createNew("sample8"));
        assertNotNull(obj);
        assertNotNull(obj.sample);

        var obj2: SampleProperties = SampleProperties(_xmlIOC.createNew("sample6"));
        assertNotNull(obj2);
        assertEquals("value1", obj2.arg1);
        assertEquals("value2", obj2.arg2);

        obj2 = SampleProperties(_xmlIOC.createNew("sample7"));
        assertNotNull(obj2.sample);

        obj2 = SampleProperties(_xmlIOC.createNew("sample9"));
        assertEquals("sampleArg", obj2.sampleArgsRef.arg1);        
    }
}
}